﻿using DH.Core;
using DH.Core.MapperHelper;
using DH.Data;
using DH.Data.ORM.EF;
using DH.Data.ORM.EF.Extensions;
using DH.Entity;
using DH.Service.ViewModels;
using DH.Service.ViewModels.Dto;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DH.Service.Domain
{
   public class MenuService: BaseService<MenuEntity, string>
    {
        protected IServiceCollection _serviceCollection;
        PrivilegeService _privilegeService;
        //OrganizeService _organizeService;
        public MenuService(IServiceCollection service, DbCoreUnitOfWork unitOfWork) : base(service, unitOfWork)
        {
            this._serviceCollection = service;
            //this._organizeService = this._serviceCollection.BuildServiceProvider().GetService<OrganizeService>();
            this._privilegeService = this._serviceCollection.BuildServiceProvider().GetService<PrivilegeService>();
        }

        public PagedData<MenuEntity> GetList(string key,string menuId, Pagination page, bool isDeveloper)
        {
            var menuQuery = GetAllSync() as IQueryable<MenuEntity>;
            menuQuery = menuQuery.Where(c => c.Id != Guid.Empty.ToString());
            if (!isDeveloper)
            {
                menuQuery = menuQuery.Where(c => c.OpenToDeveloper == false);
            }
            if (!string.IsNullOrWhiteSpace(key))
            {
                menuQuery = menuQuery.Where(c => (c.Name.Contains(key)));
            }
            if (!string.IsNullOrWhiteSpace(menuId))
            {
                var menuObj = GetSync(menuId);

                menuQuery = menuQuery.Where(c =>c.IndexId.Contains(menuObj.IndexId));
            }

            var tableEntityList = menuQuery.OrderBy(c => c.IndexId).TakePageData(page);
            return tableEntityList;
        }

        public MenuEntity Add(MenuViewModel viewModel)
        {
            MenuEntity model;
            model = DHMapper.Map<MenuViewModel, MenuEntity>(viewModel);
            model.Id = Guid.NewGuid().ToString();
            model.CreatedOn = DateTimeOffset.Now;
            model.UpdatedOn = DateTimeOffset.Now;
            model.SortIndex = GetSortIndexInParent(model);
            model.IndexId = GetParentIndexId(model.ParentId) + model.SortIndex+"." ;
            

            InsertSync(model);

            return model;


        }
        private string GetParentIndexId(string parentId)
        {
            if (parentId == "00000000-0000-0000-0000-000000000000")
            {
                return "0.";
            }
            else {
                var parentObj = GetSync(parentId);
                if (parentObj != null)
                {
                    return parentObj.IndexId;
                }
                else {
                    return "0.";
                }
            }
        }
        private int GetSortIndexInParent(MenuEntity model)
        {
            var sortInt = 0;
            var res = GetByQuery(c => c.Status == true);
            var existModel = res.Where(c=>c.SortIndex== model.SortIndex&& c.ParentId==model.ParentId&&c.Id!=model.Id).Count();
            if (existModel > 0)
            {
                var maxSortInParent = res.Where(c => c.ParentId == model.ParentId).Max(s => s.SortIndex);
                sortInt = maxSortInParent + 1;
            }
            else
            {
                sortInt = model.SortIndex;
            }
            
            return sortInt;

        }
        public MenuEntity Edit(MenuViewModel viewModel)
        {
            var oldObj = GetSync(viewModel.Id);
            var oldObjId = oldObj.Id;
            var oldObjIndexId = oldObj.IndexId;

            var entityObj = DHMapper.Map<MenuViewModel, MenuEntity>(viewModel, oldObj);
            entityObj.UpdatedOn = DateTimeOffset.Now;
            entityObj.SortIndex = GetSortIndexInParent(entityObj);
            entityObj.IndexId = GetParentIndexId(entityObj.ParentId) + entityObj.SortIndex + ".";

            UpdateSync(entityObj);

            //更换了节点排序号
            //更新所有子节点排序号
            if (oldObjIndexId!=entityObj.IndexId)
            {
                var nodes = GetByQuery(c => c.Status == true && c.IndexId.Contains(oldObjIndexId) && c.Id != oldObjId);
                foreach (var node in nodes)
                {
                    node.IndexId=node.IndexId.Replace(oldObjIndexId,entityObj.IndexId);
                }
                _unitOfWork.ExecuteAndCommit(() => {
                    _repository.Update(nodes);
                });
                
            }

            return entityObj;
        }

        public void Delete(string[] ids)
        {
            var models = GetByQuery(c => ids.Contains(c.Id));
            DeleteSync(models);
        }

        public List<SelectDto> GetMenuSelects(bool isDeveloper)
        {
            var res = GetByQuery(c => c.Status == true && c.ParentId == "00000000-0000-0000-0000-000000000000");
            if (!isDeveloper)
            {
                res = res.Where(c=>c.OpenToDeveloper==false);
            }
            res=res.OrderBy(c=>c.SortIndex);

            var selects = res.Select(c=> {
                return new SelectDto {
                    id=c.Id,
                    text=c.Name,
                    parentId=c.ParentId
                };
            }).ToList();

            return selects;

        }

        public List<SelectDto> GetMenus(bool isDeveloper)
        {
            var res = GetByQuery(c => c.Status == true);
            if (!isDeveloper)
            {
                res = res.Where(c=>c.OpenToDeveloper==false);
            }
            res=res.OrderBy(c=>c.SortIndex);
            var selects = res.Select(c => {
                return new SelectDto
                {
                    id = c.Id,
                    text = c.Name,
                    parentId = c.ParentId
                };
            }).ToList();

            return selects;
        }

        public MenuEntity GetModelById(string id)
        {
            var res = this.GetSync(id);
            return res;
        }

        public bool Enable(string id)
        {
            var userObj = GetSync(id);
            if (userObj != null)
            {
                userObj.Status = true;
                UpdateSync(userObj);
                return true;
            }
            else
            {
                return false;
            }
        }
        public bool Disable(string id) {
            var userObj = GetSync(id);
            if (userObj != null)
            {
                userObj.Status = false;
                UpdateSync(userObj);
                return true;
            }
            else
            {
                return false;
            }

        }

        public List<MenuDto> GetRootMenu(bool isDeveloper,List<string> roleMenus)
        {

            var menuDtos = GetByQuery(c => c.Status == true && c.ParentId == "00000000-0000-0000-0000-000000000000");
            if (!isDeveloper)
            {
                if (roleMenus != null && roleMenus.Any())
                {
                    menuDtos = menuDtos.Where(c => roleMenus.Contains(c.Id));
                }
                else
                { 
                    return new List<MenuDto>();
                }

            }

            
            menuDtos = menuDtos.OrderBy(c => c.SortIndex);
            var menus= menuDtos.Select(c=>new MenuDto {
                Id=c.Id,
                ParentId=c.ParentId,
                Name=c.Name,
                Url=c.Url,
                Icon=c.Icon,
                SortIndex=c.SortIndex
            }).ToList();
            
            GetChildMenus(menus,roleMenus);

            return menus;
        }
        private void GetChildMenus(List<MenuDto> mdtos,List<string> roleMenus)
        {
            foreach (var m in mdtos)
            {
                var existChildren = GetByQuery(c => c.Status == true && c.ParentId == m.Id);
                if (roleMenus != null && roleMenus.Any())
                {
                    existChildren = existChildren.Where(c=>roleMenus.Contains(c.Id));
                }
                if (existChildren.Any())
                {
                    m.ChildMenus = existChildren.OrderBy(c=>c.SortIndex).Select(c => new MenuDto {
                        Id = c.Id,
                        ParentId = c.ParentId,
                        Name = c.Name,
                        Url = c.Url,
                        Icon = c.Icon,
                        SortIndex = c.SortIndex
                    }).ToList();
                    GetChildMenus(m.ChildMenus,roleMenus);
                }
            }
        }



        #region 异步操作
        public async Task<PagedData<MenuEntity>> GetListAsync(string key, string menuId, Pagination page,bool isDeveloper)
        {
            var menuQuery = (await GetAllAsync()) as IQueryable<MenuEntity>;
            menuQuery = menuQuery.Where(c => c.Id != Guid.Empty.ToString());

            if (!isDeveloper)
            {
                menuQuery = menuQuery.Where(c=>c.OpenToDeveloper==false);
            }
            
            if (!string.IsNullOrWhiteSpace(key))
            {
                menuQuery = menuQuery.Where(c => (c.Name.Contains(key)));
            }
            if (!string.IsNullOrWhiteSpace(menuId))
            {
                var menuObj = GetSync(menuId);

                menuQuery = menuQuery.Where(c => c.IndexId.Contains(menuObj.IndexId));
            }

            var tableEntityList = menuQuery.OrderBy(c => c.IndexId).TakePageData(page);
            return tableEntityList;
        }

        public async  Task<MenuEntity> AddAsync(MenuViewModel viewModel)
        {
            MenuEntity model;
            model = DHMapper.Map<MenuViewModel, MenuEntity>(viewModel);
            model.Id = Guid.NewGuid().ToString();
            model.CreatedOn = DateTimeOffset.Now;
            model.UpdatedOn = DateTimeOffset.Now;
            model.SortIndex =await GetSortIndexInParentAsync(model);
            model.IndexId =await GetParentIndexIdAsync(model.ParentId) + model.SortIndex + ".";


            await InsertAsync(model);

            return model;


        }
        private async Task<string> GetParentIndexIdAsync(string parentId)
        {
            if (parentId == "00000000-0000-0000-0000-000000000000")
            {
                return "0.";
            }
            else
            {
                var parentObj =await GetAsync(parentId);
                if (parentObj != null)
                {
                    return parentObj.IndexId;
                }
                else
                {
                    return "0.";
                }
            }
        }
        private async Task<int> GetSortIndexInParentAsync(MenuEntity model)
        {
            var sortInt = 0;
            var res =await  GetByQueryAsync(c => c.Status == true);
            var existModel = res.Where(c => c.SortIndex == model.SortIndex && c.ParentId == model.ParentId && c.Id != model.Id).Count();
            if (existModel > 0)
            {
                var maxSortInParent = res.Where(c => c.ParentId == model.ParentId).Max(s => s.SortIndex);
                sortInt = maxSortInParent + 1;
            }
            else
            {
                sortInt = model.SortIndex;
            }

            return sortInt;

        }
        public async Task<MenuEntity> EditAsync(MenuViewModel viewModel)
        {
            var oldObj =await GetAsync(viewModel.Id);
            var oldObjId = oldObj.Id;
            var oldObjIndexId = oldObj.IndexId;

            var entityObj = DHMapper.Map<MenuViewModel, MenuEntity>(viewModel, oldObj);
            entityObj.UpdatedOn = DateTimeOffset.Now;
            entityObj.SortIndex =await GetSortIndexInParentAsync(entityObj);
            entityObj.IndexId =await GetParentIndexIdAsync(entityObj.ParentId) + entityObj.SortIndex + ".";

            await UpdateAsync(entityObj);

            //更换了节点排序号
            //更新所有子节点排序号
            if (oldObjIndexId != entityObj.IndexId)
            {
                var nodes =await GetByQueryAsync(c => c.Status == true && c.IndexId.Contains(oldObjIndexId) && c.Id != oldObjId);
                foreach (var node in nodes)
                {
                    node.IndexId = node.IndexId.Replace(oldObjIndexId, entityObj.IndexId);
                }
                await _unitOfWork.ExecuteAndCommitAsync(() => {
                    return _repository.UpdateAsync(nodes);
                });

            }

            return entityObj;
        }

        public async Task DeleteAsync(string[] ids)
        {
            var models = await GetByQueryAsync(c => ids.Contains(c.Id));
            await DeleteAsync(models);
        }

        public async Task<List<SelectDto>> GetMenuSelectsAsync(bool isDeveloper)
        {
            var res = (await GetByQueryAsync(c => c.Status == true && c.ParentId == "00000000-0000-0000-0000-000000000000"));
            if (!isDeveloper)
            {
                res = res.Where(c=>c.OpenToDeveloper==false);
            }
            res=res.OrderBy(c => c.SortIndex);

            var selects = res.Select(c => {
                return new SelectDto
                {
                    id = c.Id,
                    text = c.Name,
                    parentId = c.ParentId
                };
            }).ToList();

            return selects;

        }

        public async Task<List<SelectDto>> GetMenusAsync(bool isDeveloper)
        {
            var res = (await GetByQueryAsync(c => c.Status == true));
            if (!isDeveloper)
            {
                res = res.Where(c=>c.OpenToDeveloper==false);
            }
            res=res.OrderBy(c => c.SortIndex);
            var selects = res.Select(c => {
                return new SelectDto
                {
                    id = c.Id,
                    text = c.Name,
                    parentId = c.ParentId
                };
            }).ToList();

            return selects;
        }

        public async Task<MenuEntity> GetModelByIdAsync(string id)
        {
            var res = await this.GetAsync(id);
            return res;
        }

        public async  Task<bool> EnableAsync(string id)
        {
            var userObj =await GetAsync(id);
            if (userObj != null)
            {
                userObj.Status = true;
                await UpdateAsync(userObj);
                return true;
            }
            else
            {
                return false;
            }
        }
        public async Task<bool> DisableAsync(string id)
        {
            var userObj =await GetAsync(id);
            if (userObj != null)
            {
                userObj.Status = false;
                await UpdateAsync(userObj);
                return true;
            }
            else
            {
                return false;
            }

        }
        public async Task<List<MenuDto>> GetRootMenuAsync(bool isDeveloper, List<string> roleMenus)
        {

            var menuDtos =await GetByQueryAsync(c => c.Status == true && c.ParentId == "00000000-0000-0000-0000-000000000000");
            if (!isDeveloper)
            {
                //不管开发者有没有给角色分配应用系统菜单，非开发者用户菜单都排除OpenToDeveloper==true
                menuDtos = menuDtos.Where(c=>c.OpenToDeveloper==false);

                if (roleMenus != null && roleMenus.Any())
                {
                    menuDtos = menuDtos.Where(c => roleMenus.Contains(c.Id));
                }
                else
                {
                    return new List<MenuDto>();
                }

            }


            menuDtos = menuDtos.OrderBy(c => c.SortIndex);
            var menus = menuDtos.Select(c => new MenuDto
            {
                Id = c.Id,
                ParentId = c.ParentId,
                Name = c.Name,
                Url = c.Url,
                Icon = c.Icon,
                SortIndex = c.SortIndex
            }).ToList();

            GetChildMenus(menus, roleMenus);

            return menus;
        }
        #endregion

    }
}
